﻿using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PublicClass
{
    public static class CadUnits
    {
        public static Document CurDoc()
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            if (doc == null)
                doc = Application.DocumentManager.CurrentDocument;
            return doc;
        }

        public static Database CurDb()
        {
            return CurDoc().Database;
        }
        public static Editor CurEdit()
        {
            return CurDoc().Editor;
        }
        public static void WriteMessage(string message, params object[] parameter)
        {
            if (CurDoc() == null)
                return;

            if (CurDoc().Editor == null)
                return;

            try
            {
                CurDoc().Editor.WriteMessage(message, parameter);
            }
            catch (Exception)
            {
            }
        }

        public static void WriteMessage(string message)
        {
            if (CurDoc() == null)
                return;

            if (CurDoc().Editor == null)
                return;

            try
            {
                CurDoc().Editor.WriteMessage(message);
            }
            catch (Exception)
            {
            }
        }

        public static Transaction StartTransaction()
        {
            return CurDb().TransactionManager.StartTransaction();
        }


        #region cad的系统变量

        /// <summary>
        /// 设置cad系统变量
        /// </summary>
        /// <param name="name">变量名称</param>
        /// <param name="value">变量值</param>
        public static void SetSystemVariable(string name, object value)
        {
            Autodesk.AutoCAD.ApplicationServices.Application.SetSystemVariable(name, value);
        }

        /// <summary>
        /// 获取cad系统变量
        /// </summary>
        /// <param name="name">变量名称</param>
        /// <returns>返回对应变量的参数值</returns>
        public static object GetSystemVariable(string name)
        {
            return Autodesk.AutoCAD.ApplicationServices.Application.GetSystemVariable(name);
        }
        #endregion

        /// <summary>
        /// double转字符串 保留位数0
        /// </summary>
        /// <param name="value">传入值</param>
        /// <param name="nPre">传入保留位数</param>
        /// <returns></returns>
        public static string DoubleToStr0(double value, int nPre = 2)
        {
            return value.ToString("f" + nPre.ToString());
        }

        public const string Textstyle = "Textstyle";           // 文字样式 字符串
        /// <summary>
        /// 取当前字体样式的ID
        /// </summary>
        /// <returns></returns>
        public static ObjectId GetCurTextStyleID()
        {
            //读取当前字型
            string sCurrentStyle = CadUnits.GetSystemVariable(CadUnits.Textstyle) as string;
            if (null == sCurrentStyle)
                return ObjectId.Null;
            //
            ObjectId styleID = ObjectId.Null;


            // 定义一个指向当前数据库的事务处理
            using (OpenCloseTransaction trans = HostApplicationServices.WorkingDatabase.TransactionManager.StartOpenCloseTransaction())
            {
                // 以读方式打开块表
                TextStyleTable bt = (TextStyleTable)trans.GetObject(HostApplicationServices.WorkingDatabase.TextStyleTableId, OpenMode.ForRead);
                if (null != bt)
                {
                    if (bt.Has(sCurrentStyle))
                        styleID = bt[sCurrentStyle];
                }
            }

            return styleID;

        }

        /// <summary>
        /// 获取对应文字样式的宽度比例因子
        /// </summary>
        /// <param name="textStyle">传入的文字样式id</param>
        /// <returns>返回对应的的宽度比例因子</returns>
        public static double GetTextStyleWidthFactor(ObjectId textStyle)
        {
            double dWidthFactor = 0.6;

            if (textStyle.IsNull)
            {
                return dWidthFactor;
            }

            Database db = CadUnits.CurDb();
            if (null == db)
                return dWidthFactor;
            // textStyle.Database;

            // 定义一个指向当前数据库的事务处理
            using (OpenCloseTransaction trans = db.TransactionManager.StartOpenCloseTransaction())
            {
                // 以读方式打开块表
                TextStyleTableRecord bt = (TextStyleTableRecord)trans.GetObject(textStyle, OpenMode.ForRead);
                if (null != bt)
                {
                    dWidthFactor = bt.XScale;
                }
            }

            return dWidthFactor;
        }

        /// <summary>
        /// 获取文字宽度 空或者空格返回 0
        /// </summary>
        /// <param name="str">传入文字字符串</param>
        /// <param name="textHeigth">传入文字高度</param>
        /// <param name="dTextWidth">传出文字宽度</param>
        /// <param name="textStyle">传入文字样式，默认null取当前样式</param>
        /// <returns></returns>
        public static bool GetTextWidth(string str, double textHeigth, ref double dTextWidth, ObjectId? textStyle = null)
        {
            ObjectId textStyleCur = textStyle ?? GetCurTextStyleID();

            if (string.IsNullOrEmpty(str) || string.IsNullOrWhiteSpace(str))
            {
                dTextWidth = 0;
                return true;
            }
            //////////////////////////////////////////////////////////////////////////
            double dWidthFactor = GetTextStyleWidthFactor(textStyleCur);

            //////////////////////////////////////////////////////////////////////////
            MText text = NewMtext(str, Point3d.Origin, textHeigth, AttachmentPoint.MiddleCenter, 0, null, textStyleCur);

            if (null != text)
            {
                Extents3d ext = text.GeometricExtents;
                dTextWidth = Math.Abs(ext.MaxPoint.X - ext.MinPoint.X);

                return true;
            }

            return false;
        }

        /// <summary>
        /// new一个Mtext，不加入数据库，空或者空格则返回null
        /// </summary>
        /// <param name="str">传入字符串</param>
        /// <param name="pt">传入插入位置点 wcs</param>
        /// <param name="dblTxtH">文字高度</param>
        /// <param name="at">文字对齐方式</param>
        /// <param name="dRotAng">文字旋转方向</param>
        /// <param name="strLayer">文字所在图层</param>
        /// <param name="textstyleID">文字样式id</param>
        /// <returns></returns>
        public static MText NewMtext(string str, Point3d pt, double dblTxtH, AttachmentPoint at = AttachmentPoint.MiddleCenter, double dRotAng = 0, string strLayer = null, ObjectId? textstyleID = null)
        {
            if (string.IsNullOrWhiteSpace(str) || string.IsNullOrEmpty(str))
                return null;

            MText mText = new MText();
            mText.Location = pt;
            mText.Contents = str;
            mText.TextHeight = dblTxtH;
            mText.Attachment = at;
            mText.TextStyleId = textstyleID ?? GetCurTextStyleID();
            mText.FlowDirection = FlowDirection.ByStyle;
            if (null != strLayer)
                mText.Layer = strLayer;
            // 文字角度 Mtext 相对于ucs的0度 中望有几个版本相对于wcs 和cad有区别,后来我们和中望反馈,他们及时的修正了,具体哪个回头可以告诉大家
            mText.TransformBy(Matrix3d.Rotation(dRotAng, Vector3d.ZAxis, pt));
            return mText;
        }

        public static bool AppendEntity(Transaction ts, Entity ent, BlockTableRecord blkRecord = null)
        {
            if (null == ts || null == ent)
                return false;

            if (null == blkRecord)
            {
                BlockTable blTable = ts.GetObject(CurDb().BlockTableId, OpenMode.ForRead) as BlockTable;

                blkRecord = ts.GetObject(blTable[BlockTableRecord.ModelSpace], OpenMode.ForWrite) as BlockTableRecord;
            }
            blkRecord.AppendEntity(ent); // 加入到模型空间

            ts.AddNewlyCreatedDBObject(ent, true);

            return true;
        }
        public static bool AppendEntity(Entity ent, BlockTableRecord blkRecord = null, Transaction ts = null)
        {
            if (null == ent)
                return false;
            bool bNewTs = false;
            if (null == ts)
            {
                ts = CadUnits.StartTransaction();
                bNewTs = true;
            }

            AppendEntity(ts, ent, blkRecord);

            if (bNewTs)
            {
                ts.Commit();
                ts.Dispose();
            }

            return true;
        }

        public static bool EraseEnt(ObjectId id, Transaction ts = null)
        {
            bool bNew = false;
            if (null == ts)
            {
                bNew = true;
                ts = CadUnits.StartTransaction();
            }

            DBObject obj = ts.GetObject(id, OpenMode.ForWrite);
            if (null != obj)
                obj.Erase();
            if (bNew)
            {
                ts.Commit();
                ts.Dispose();
            }
            return true;
        }

        public static bool EraseEnt(List<ObjectId> lst, Transaction ts = null)
        {
            bool bNew = false;
            if (null == ts)
            {
                bNew = true;
                ts = CadUnits.StartTransaction();
            }

            foreach (ObjectId id in lst)
            {
                EraseEnt(id, ts);
            }

            if (bNew)
            {
                ts.Commit();
                ts.Dispose();
            }

            return true;
        }
        public static Point2d Pt3DTo2D(Point3d pt)
        {
            return new Point2d(pt.X, pt.Y);
        }
        public static ObjectId DrawExtent(Transaction ts, Extents3d ex)
        {
            ObjectId id = ObjectId.Null;
            Polyline pl = new Polyline();
            pl.AddVertexAt(pl.NumberOfVertices, Pt3DTo2D(ex.MinPoint), 0, 0, 0);
            pl.AddVertexAt(pl.NumberOfVertices, Pt3DTo2D(ex.MinPoint + Vector3d.XAxis * (ex.MaxPoint.X - ex.MinPoint.X)), 0, 0, 0);
            pl.AddVertexAt(pl.NumberOfVertices, Pt3DTo2D(ex.MaxPoint), 0, 0, 0);
            pl.AddVertexAt(pl.NumberOfVertices, Pt3DTo2D(ex.MaxPoint - Vector3d.XAxis * (ex.MaxPoint.X - ex.MinPoint.X)), 0, 0, 0);
            pl.Closed = true;

            AppendEntity(ts, pl);
            id = pl.ObjectId;
            return id;
        }

        public static ObjectId CreatCircle(Transaction ts, Point3d ptCenter, double dRadius = 2)
        {
            Circle circle = new Circle();
            circle.Center = ptCenter;
            circle.Radius = dRadius;

            AppendEntity(ts, circle);

            return circle.ObjectId;
        }

        // Summary: 颜色枚举。
        public enum ColorEnum
        {
            kByblock = 0,       // 随块
            kRed = 1,           // 红色
            kYellow = 2,        // 黄色
            kGreen = 3,         // 绿色
            kCyan = 4,          // 青色
            kBlue = 5,          // 蓝色
            kMagenta = 6,       // 红紫色		
            kWhite = 7,         // 白色
            kBylayer = 256      // 随层
        };

        /// <summary>
        /// 创建图层的基本函数
        /// </summary>
        /// <param name="ts"></param>
        /// <param name="layerName"></param>
        /// <param name="nColor"></param>
        /// <returns></returns>
        public static bool CreateLayer(Transaction ts, string layerName, int nColor = 7)
        {
            if (null == ts || null == layerName)
                return false;

            LayerTable blTable = ts.GetObject(CurDb().LayerTableId, OpenMode.ForWrite) as LayerTable;

            if (blTable.Has(layerName))
                return true;

            try
            {
                LayerTableRecord layerRecord = new LayerTableRecord();
                layerRecord.Name = layerName;
                layerRecord.Color =
                    Autodesk.AutoCAD.Colors.Color.FromColorIndex(Autodesk.AutoCAD.Colors.ColorMethod.ByAci, (short)nColor);

                blTable.Add(layerRecord);

                ts.AddNewlyCreatedDBObject(layerRecord, true);
            }
            catch (Exception e)
            {
                ;// 输出一些错误提示
                Autodesk.AutoCAD.ApplicationServices.Document doc = Application.DocumentManager.MdiActiveDocument;
                doc.Editor.WriteMessage("\n 错误提示{0},{1} ", e.Message, e.ToString());
            }


            return true;
        }


        public static ObjectId CreatBlk(Transaction ts, string blkName, List<ObjectId> lst)
        {
            ObjectId idBlkRecord = ObjectId.Null;
            if (null == ts || null == blkName || lst == null)
            {
                return idBlkRecord;
            }

            BlockTable blkTbl = ts.GetObject(CadUnits.CurDb().BlockTableId, OpenMode.ForWrite) as BlockTable;
            try
            {
                if (!blkTbl.Has(blkName))
                {
                    BlockTableRecord blk = new BlockTableRecord();
                    blk.Name = blkName;
                    foreach (var item in lst)
                    {
                        Entity ent = ts.GetObject(item, OpenMode.ForRead) as Entity;
                        blk.AppendEntity((Entity)ent.Clone());
                    }

                    blkTbl.Add(blk);
                    idBlkRecord = blk.ObjectId;

                    ts.AddNewlyCreatedDBObject(blk, true);
                }
                else
                    idBlkRecord = blkTbl[blkName];

            }
            catch (Exception e)
            {
            }


            return idBlkRecord;
        }

        public static ObjectId CreatBlk(Transaction ts, string blkName, List<Entity> lst, Point3d? ptCenter = null)
        {
            ObjectId idBlkRecord = ObjectId.Null;
            if (null == ts || null == blkName || null == lst)
                return idBlkRecord;

            if (lst.Count < 1)
                return idBlkRecord;

            BlockTable blkTbl = ts.GetObject(CurDb().BlockTableId, OpenMode.ForWrite) as BlockTable;

            try
            {
                if (!blkTbl.Has(blkName))
                {
                    BlockTableRecord blk = new BlockTableRecord();
                    blk.Name = blkName;
                    Extents3d ex = new Extents3d();
                    foreach (var item in lst)
                    {
                        ex.AddExtents(item.GeometricExtents);
                        blk.AppendEntity(item);
                    }

                    Point3d pt = ptCenter ?? ex.GetCenterPoint();
                    blk.Origin = pt;
                    blkTbl.Add(blk);
                    idBlkRecord = blk.ObjectId;

                    ts.AddNewlyCreatedDBObject(blk, true);
                }
                else
                    idBlkRecord = blkTbl[blkName];

            }
            catch (Exception e)
            {
            }

            return idBlkRecord;
        }

        #region 图块相关函数

        public static ObjectId InsertBlockFromDwgtoDatabase(string sDwgPath, string sBlockName = "", Database pToDb = null)
        {
            ObjectId idRecord = ObjectId.Null;
            if (null == sBlockName || sBlockName.Length == 0)
            {
                sBlockName = System.IO.Path.GetFileNameWithoutExtension(sDwgPath);
            }

            Database db = new Database(false, true);
            try
            {
                db.ReadDwgFile(sDwgPath, FileOpenMode.OpenTryForReadShare, true, "");

                idRecord = InSertBlockFromDwgToCurDb(db, sBlockName, pToDb);
            }
            catch (Exception e)
            {
                CadUnits.CurEdit().WriteMessage($"\n{e.Message}");
            }


            return idRecord;
        }

        public static ObjectId InSertBlockFromDwgToCurDb(Database pDb, string sBlockName = "", Database pDestDb = null)
        {
            ObjectId BlockId = ObjectId.Null;

            if (pDb == null)
            {
                return BlockId;
            }

            if (null == sBlockName || sBlockName.Length == 0)
            {
                sBlockName = "*U";
            }

            if (null == pDestDb)
                pDestDb = CadUnits.CurDb();
            using (CadUnits.CurDoc().LockDocument())
            {
                BlockId = pDestDb.Insert(sBlockName, pDb, true);
            }

            return BlockId;

        }
        #endregion
        public static void RunLspCmd(string cmd)
        {
            StringBuilder sb = new StringBuilder();
            var cmdActive = GetSystemVariable("cmdactive");
            if (null != cmdActive)
            {
                int n = Convert.ToInt32(cmdActive);

                if (n != 0)
                {
                    char c = (char)0x1b;

                    sb.Append(c);
                    sb.Append(c);
                    sb.Append(c);
                    sb.Append(cmd);
                }
            }

            if (sb.Length == 0)
                sb.Append(cmd);

            sb.Append(" ");

            CadUnits.CurDoc().SendStringToExecute(sb.ToString(), true, false, false);
        }


        /// <summary>
        /// 创建自用表格样式
        /// </summary>
        /// <returns></returns>
        static public ObjectId CreateWTableStyle()
        {
            ObjectId id = ObjectId.Null;
            string tableName = "WFTableStyle";
            string tableStandName = "Standard";
            using (Transaction ts = StartTransaction())
            {
                DBObject obj = ts.GetObject(CurDb().TableStyleDictionaryId, OpenMode.ForRead);
                if (null == obj)
                    return id;
                DBDictionary dic = obj as DBDictionary;
                if (null == dic)
                    return id;
                if (dic.Contains(tableName))
                {
                    id = dic.GetAt(tableName);
                }
                else
                {
                    id = dic.GetAt(tableStandName);

                    TableStyle style = ts.GetObject(id, OpenMode.ForRead) as TableStyle;
                    if (null == style)
                        return id;
                    TableStyle newStyle = style.Clone() as TableStyle;
                    dic.UpgradeOpen();
                    dic.SetAt(tableName, newStyle);
                    newStyle.Name = tableName;
                    newStyle.SetTextHeight(0.01, (int)(RowType.TitleRow | RowType.DataRow | RowType.HeaderRow));
                    newStyle.HorizontalCellMargin = 0.01;
                    newStyle.VerticalCellMargin = 0.01;

                    newStyle.SetAlignment(CellAlignment.MiddleCenter, (int)(RowType.TitleRow | RowType.DataRow | RowType.HeaderRow));

                    id = newStyle.ObjectId;

                    ts.AddNewlyCreatedDBObject(newStyle, true);

                    ts.Commit();
                }
            }

            return id;
        }
    }

    public static class Point3DEx
    {
        private const double Tol = 0.001;
        public static Point3d GetCenterPoint(this Extents3d ex)
        {
            Vector3d vec = ex.MaxPoint - ex.MinPoint;

            return ex.MinPoint + vec / 2.0;
        }
        //         Point3d ptMin = node.m_bd.GetMinPoint();
        //         Point3d ptMax = node.m_bd.GetMaxPoint(); ContainsXY
        public static Point3d GetMinPoint(this Extents3d ex)
        {
            return ex.MinPoint;
        }
        public static Point3d GetMaxPoint(this Extents3d ex)
        {
            return ex.MaxPoint;
        }

        public static double Width(this Extents3d ex)
        {
            return ex.MaxPoint.X - ex.MinPoint.X;
        }

        public static double Height(this Extents3d ex)
        {
            return ex.MaxPoint.Y - ex.MinPoint.Y;
        }

        public static bool ContainsXY(this Extents3d ex, Extents3d exOther)
        {
            if (ex.ContainsXY(exOther.MinPoint) && ex.ContainsXY(exOther.MaxPoint))
                return true;

            return false;
        }
        public static bool ContainsXY(this Extents3d ex, Point3d pt)
        {
            Point3d MinPoint = ex.MinPoint;
            Point3d MaxPoint = ex.MaxPoint;

            bool bX = MinPoint.X < pt.X + Tol && pt.X < MaxPoint.X + Tol;
            bool bY = MinPoint.Y < pt.Y + Tol && pt.Y < MaxPoint.Y + Tol;
            if (bX && bY)
                return true;

            return false;
        }

        public static bool ExtsIsIntersect(this Extents3d ex, Extents3d exOther)
        {
            // 求出当前外包区域的四个边界
            Point3d ptRightTop = ex.MaxPoint;
            Point3d ptLeftBottom = ex.MinPoint;
            double dCurExtTop = ptRightTop.Y;
            double dCurExtBottom = ptLeftBottom.Y;
            double dCurExtLeft = ptLeftBottom.X;
            double dCurExtRight = ptRightTop.X;

            // 求出另一个外包区域的四个边界
            Point3d ptOtherRightTop = exOther.MaxPoint;
            Point3d ptOtherLeftBottom = exOther.MinPoint;
            double dOtherExtTop = ptOtherRightTop.Y;
            double dOtherExtBottom = ptOtherLeftBottom.Y;
            double dOtherExtLeft = ptOtherLeftBottom.X;
            double dOtherExtRight = ptOtherRightTop.X;

            // 利用反向思维，判断不相交的情况，其它的则相交
            // 对于矩形的四条边做如下判断
            //(r1.top < r2.bottom || r1.right < r2.left || r1.bottom > r2.top || r1.left > r2.right)
            if (dCurExtTop < dOtherExtBottom || dCurExtRight < dOtherExtLeft || dCurExtBottom > dOtherExtTop || dCurExtLeft > dOtherExtRight)
            {
                return false;
            }
            return true;
        }

        public static bool IsSameSize(this Extents3d ex, Extents3d exOther)
        {
            double dWidth = ex.Width();
            double dHeight = ex.Height();
            double dWidthOther = exOther.Width();
            double dHeightOther = exOther.Height();

            if (PublicNoCad.EqualDouble(dWidth, dWidthOther) && PublicNoCad.EqualDouble(dHeight, dHeightOther))
                return true;

            return false;
        }
    }
}
